﻿using GDI_应用案例.Pages.Controls;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GDI_应用案例.Pages._3D图像
{
    public class HologramControl : Control
    {
        List<IFace> faces = new List<IFace>();
        Bitmap[] _back;
        float angleX, angleY;
        Point lastMouse;
        bool dragging;
        readonly float[][] rotationMatrix = new float[3][];

        System.Windows.Forms.Timer rotationTimer;
        float autoRotationSpeedX = 0.03f; // X轴旋转速度
        float autoRotationSpeedY = 0.06f; // Y轴旋转速度
        public HologramControl()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

            this.ClientSize = new Size(800, 600);
            this.MouseDown += (s, e) => { dragging = true; lastMouse = e.Location; };
            this.MouseUp += (s, e) => dragging = false;
            this.MouseMove += HologramControl_MouseMove;

            rotationTimer = new System.Windows.Forms.Timer();
            rotationTimer.Interval = 120;
            rotationTimer.Tick += (s, e) =>
            {
                if (_back != null)
                {
                    if (++_curBackIndex >= _back.Length)
                        _curBackIndex = 0;
                }
               
                if (faces.Count == 0) return;
                if (!dragging) 
                {
                    angleX += autoRotationSpeedX;
                    angleY += autoRotationSpeedY;
                    Invalidate(); 
                }
            };
            rotationTimer.Start(); 
        }
        public Point3D FaceSize { get; set; } = new Point3D(100,100,100);
        private void HologramControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (dragging)
            {
                angleY += (e.X - lastMouse.X) * 0.01f;
                angleX += (e.Y - lastMouse.Y) * 0.01f;
                lastMouse = e.Location;
                Invalidate();
            }
        }
        public void LoadImage(string[][] filePaths)
        {
            if (filePaths.GetLength(0) != 6)
                throw new ArgumentException("必须有6组图形");
            Bitmap[][] bitmaps = new Bitmap[6][];
            for (int i = 0; i < filePaths.GetLength(0); i++)
            {
                var arr = filePaths[i];
                if (arr.Length < 1)
                    throw new ArgumentException("每组图形必须大于等于1");
                bitmaps[i] = new Bitmap[arr.Length];
                for (int j = 0; j < arr.Length; j++)
                    bitmaps[i][j] = new Bitmap(arr[j]);
            }
            LoadImage(bitmaps);
        }
        public void LoadImage(Bitmap[][] bitmaps)
        {
            if (bitmaps.Length != 6)
                throw new ArgumentException("数量必须等于6");

            faces.Clear();

            // 前/后面
            CreateFace(new Point3D(-FaceSize.X, -FaceSize.Y, FaceSize.Z), new Point3D(FaceSize.X, -FaceSize.Y, FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, FaceSize.Z), new Point3D(-FaceSize.X, FaceSize.Y, FaceSize.Z), bitmaps[0]);     // Front
            CreateFace(new Point3D(FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(-FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(-FaceSize.X, FaceSize.Y, -FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, -FaceSize.Z), bitmaps[1]); // Back

            // 左/右面
            CreateFace(new Point3D(-FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(-FaceSize.X, -FaceSize.Y, FaceSize.Z), new Point3D(-FaceSize.X, FaceSize.Y, FaceSize.Z), new Point3D(-FaceSize.X, FaceSize.Y, -FaceSize.Z), bitmaps[2]); // Left
            CreateFace(new Point3D(FaceSize.X, -FaceSize.Y, FaceSize.Z), new Point3D(FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, -FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, FaceSize.Z), bitmaps[3]);   // Right

            // 上/下面
            CreateFace(new Point3D(-FaceSize.X, FaceSize.Y, FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, FaceSize.Z), new Point3D(FaceSize.X, FaceSize.Y, -FaceSize.Z), new Point3D(-FaceSize.X, FaceSize.Y, -FaceSize.Z), bitmaps[4]);     // Top
            CreateFace(new Point3D(-FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(FaceSize.X, -FaceSize.Y, -FaceSize.Z), new Point3D(FaceSize.X, -FaceSize.Y, FaceSize.Z), new Point3D(-FaceSize.X, -FaceSize.Y, FaceSize.Z), bitmaps[5]);// Bottom

            this.Invalidate();
        }
        void CreateFace(Point3D a, Point3D b, Point3D c, Point3D d,Bitmap[] bitmaps)
        {
            IFace face = new Face
            {
                Points = new[] { a, b, c, d },
                Normal = CalculateNormal(a, b, c),
                Textures = bitmaps
            };
            faces.Add(face);
        }

        public void LoadBackImages(string[] files) => LoadBackImages(files.Select(t => new Bitmap(t)).ToArray());
        public void LoadBackImages(Bitmap[] bitmaps)
        {
            _back = bitmaps;
        }
        private int _curBackIndex = 0;
        protected override void OnPaint(PaintEventArgs e)
        {
            if (faces.Count == 0) return;
            e.Graphics.Smooth();
            UpdateRotationMatrix();

            // 获取可见面并按深度排序（从远到近）
            var visibleFaces = faces.Where(f => IsFaceVisible(f))
                                  .OrderByDescending(f => GetFaceDepth(f));

            if(_back!=null && _back.Length > 0)
                e.Graphics.DrawImage(_back[_curBackIndex], this.ClientRectangle);


            using (var pen = new Pen(Color.Black, 1))
            {
                foreach (var face in visibleFaces) // 只遍历可见面
                {
                    // 投影顶点到2D
                    PointF[] points = face.Points.Select(p => Project(p)).ToArray();

                    // 绘制纹理
                    if (face.Texture != null && points.Length >= 3)
                    {
                        try
                        {
                            // 使用仿射变换映射纹理到前三个点定义的平行四边形
                            PointF[] destPoints = {
                        points[0],  // 纹理左上角
                        points[1],  // 纹理右上角
                        points[3]   // 纹理左下角
                    };
                            e.Graphics.DrawImage(face.Texture, destPoints);
                        }
                        catch { /* 处理可能的绘制异常 */ }
                    }

                    // 绘制面边框
                    e.Graphics.DrawPolygon(pen, points);
                }
            }
        }

        // 投影方法调整（增加缩放系数）
        PointF Project(Point3D p)
        {
            const float SCALE = 1f; // 调整此值控制显示大小
            var rotated = RotateVector(p);
            return new PointF(
                rotated.X * SCALE + ClientSize.Width / 2,
                rotated.Y * SCALE + ClientSize.Height / 2
            );
        }
        bool IsFaceVisible(IFace face)
        {
            var normal = RotateVector(face.Normal);
            return normal.Z > 0;
        }

        float GetFaceDepth(IFace face)
        {
            return face.Points.Average(p => RotateVector(p).Z);
        }

        Point3D RotateVector(Point3D v)
        {
            return new Point3D(
                v.X * rotationMatrix[0][0] + v.Y * rotationMatrix[0][1] + v.Z * rotationMatrix[0][2],
                v.X * rotationMatrix[1][0] + v.Y * rotationMatrix[1][1] + v.Z * rotationMatrix[1][2],
                v.X * rotationMatrix[2][0] + v.Y * rotationMatrix[2][1] + v.Z * rotationMatrix[2][2]
            );
        }

        void UpdateRotationMatrix()
        {
            float cosX = (float)Math.Cos(angleX), sinX = (float)Math.Sin(angleX);
            float cosY = (float)Math.Cos(angleY), sinY = (float)Math.Sin(angleY);

            rotationMatrix[0] = new[] { cosY, 0f, sinY };
            rotationMatrix[1] = new[] { sinX * sinY, cosX, -sinX * cosY };
            rotationMatrix[2] = new[] { -cosX * sinY, sinX, cosX * cosY };
        }

         Point3D CalculateNormal(Point3D a, Point3D b, Point3D c)
        {
            var u = new Point3D(b.X - a.X, b.Y - a.Y, b.Z - a.Z);
            var v = new Point3D(c.X - a.X, c.Y - a.Y, c.Z - a.Z);
            return new Point3D(
                u.Y * v.Z - u.Z * v.Y,
                u.Z * v.X - u.X * v.Z,
                u.X * v.Y - u.Y * v.X
            );
        }
    }
}
